package seu.xml.validator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import seu.xml.analysis.Diagram;
import seu.xml.analysis.UMLERDiagram;
import seu.xml.analysis.UMLEntity;
import seu.xml.analysis.UMLRelation;
import seu.xml.validator.ArchitectureMetaModel.UMLetDiagram;

public class GlobalCons {
	public static boolean verified;// 是否验证通过

	public static boolean cmpnDiagram;// 是否输入了组件图
	public static ArrayList<String> cmpnDiagramPath = new ArrayList<String>();// 组件图路径
	public static boolean sqcDiagram;// 顺序图
	public static ArrayList<String> sqcDiagramPath = new ArrayList<String>();
	public static boolean syssqcDiagram;// 系统顺序图
	public static ArrayList<String> syssqcDiagramPath= new ArrayList<String>();
	public static boolean actDiagram;// 活动图
	public static ArrayList<String> actDiagramPath = new ArrayList<String>();
	public static boolean stateDiagram;// 状态图
	public static ArrayList<String> stateDiagramPath = new ArrayList<String>();
	public static boolean ucDiagram;// 用例图
	public static ArrayList<String> ucDiagramPath = new ArrayList<String>();
	public static boolean pkgDiagram;// 包图
	public static ArrayList<String> pkgDiagramPath = new ArrayList<String>();
	public static boolean clsDiagram;// 类图
	public static ArrayList<String> clsDiagramPath = new ArrayList<String>();
	public static boolean dplmtDiagram;// 部署图
	public static ArrayList<String> dplmtDiagramPath = new ArrayList<String>();
	public static boolean constraintFile;// 补充约束文件LTL
	public static ArrayList<String> constraintFilePath = new ArrayList<String>();
	public static Map<String, UMLetDiagram> all_files = new HashMap<String, UMLetDiagram>();
	public static Map<String, UMLetDiagram> all_filenames = new HashMap<String, UMLetDiagram>();

	private static final String timeConstraint = "time=";
	public static boolean time_constraint;// 是否有时间约束
	private static final String resourceConstraint = "resource=";
	public static boolean resource_constraint;// 是否有资源约束
	public static boolean LTL_constraint;// 是否输入了LTL

	public static final String[] commet = { "完整性1: 架构必备组件图", "完整性2: 4+1视图完备",
			"完整性3: 所有UMLet视图完备", "完整性4: 每个组件均有类图，每个系统操作均有顺序图",
			"层次性1: 组件无直接循环依赖", /* "层次性2: 符合正交体系" */null, "层次性2: 单向依赖",
			"结构性1: 定义了组件接口", "结构性2: 组件图和系统顺序图中实体、关系说明不留空",
			"结构性3: 使用Notation标注", "功能性1: 时间约束", "功能性2: 资源约束", "功能性3: LTL约束" };

	public static String getCommet(int i) {
		return commet[i];
	}

	public static boolean fileInputed(String file) {
		if (all_files == null) {
			all_files = new HashMap<String, UMLetDiagram>();
			return false;
		}
		return all_files.containsKey(file);
	}

	/**
	 * input all files inside the folder
	 * 
	 * @param folder
	 */
	public static void input(File folder) {
		File[] f = folder.listFiles();
		ArrayList<File> ff = new ArrayList<File>();
		for (File fi : f)
			ff.add(fi);
		ArrayList<File> allFiles = new ArrayList<File>();
		while (ff.size() > 0) {
			File file = ff.remove(0);
			if (!file.isDirectory())
				allFiles.add(file);
			else {
				for (File fb : file.listFiles())
					ff.add(fb);
			}
		}
		for (File file : allFiles) {
			String fileName = file.getName();
			UMLetDiagram type = null;
			if (fileName.endsWith(".sqc.uxf")) {
				type = UMLetDiagram.sqc;
			} else if (fileName.endsWith(".act.uxf")) {
				type = UMLetDiagram.act;
			} else if (fileName.endsWith(".cls.uxf")) {
				type = UMLetDiagram.cls;
			} else if (fileName.endsWith(".cpnt.uxf")) {
				type = UMLetDiagram.cpnt;
			} else if (fileName.endsWith(".dplmt.uxf")) {
				type = UMLetDiagram.dplmt;
			} else if (fileName.endsWith(".pkg.uxf")) {
				type = UMLetDiagram.pkg;
			} else if (fileName.endsWith(".state.uxf")) {
				type = UMLetDiagram.state;
			} else if (fileName.endsWith(".ssqc.uxf")) {
				type = UMLetDiagram.syssqc;
			} else if (fileName.endsWith(".uc.uxf")) {
				type = UMLetDiagram.uc;
			} else if (fileName.indexOf(".cons") > 0) {
				type = UMLetDiagram.constraint;
			}
			if (type != null)
				set(file.getAbsolutePath(), fileName, type);
		}
	}

	public static void init() {
		verified = false;
		cmpnDiagram = false;
		cmpnDiagramPath = new ArrayList<String>();
		sqcDiagram = false;
		sqcDiagramPath = new ArrayList<String>();
		syssqcDiagram = false;
		syssqcDiagramPath = new ArrayList<String>();
		actDiagram = false;
		actDiagramPath = new ArrayList<String>();
		stateDiagram = false;
		stateDiagramPath = new ArrayList<String>();
		ucDiagram = false;
		ucDiagramPath = new ArrayList<String>();
		pkgDiagram = false;
		pkgDiagramPath = new ArrayList<String>();
		clsDiagram = false;
		clsDiagramPath = new ArrayList<String>();
		dplmtDiagram = false;
		dplmtDiagramPath = new ArrayList<String>();
		constraintFile = false;
		constraintFilePath = new ArrayList<String>();
		all_files = new HashMap<String, UMLetDiagram>();
		all_filenames = new HashMap<String, UMLetDiagram>();
		time_constraint = false;
		resource_constraint = false;
		LTL_constraint = false;
	}

	public static void set(String file, String fileName, UMLetDiagram type) {
		if (type.equals(UMLetDiagram.pkg)) {
			pkgDiagram = true;
			pkgDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.pkg);
			all_filenames.put(fileName, UMLetDiagram.pkg);
		} else if (type.equals(UMLetDiagram.cls)) {
			clsDiagram = true;
			clsDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.cls);
			all_filenames.put(fileName, UMLetDiagram.cls);
		} else if (type.equals(UMLetDiagram.act)) {
			actDiagram = true;
			actDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.act);
			all_filenames.put(fileName, UMLetDiagram.act);
		} else if (type.equals(UMLetDiagram.state)) {
			stateDiagram = true;
			stateDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.state);
			all_filenames.put(fileName, UMLetDiagram.state);
		} else if (type.equals(UMLetDiagram.cpnt)) {
			cmpnDiagram = true;
			cmpnDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.cpnt);
			all_filenames.put(fileName, UMLetDiagram.cpnt);
		} else if (type.equals(UMLetDiagram.sqc)) {
			sqcDiagram = true;
			sqcDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.sqc);
			all_filenames.put(fileName, UMLetDiagram.sqc);
			if (!time_constraint)
				time_constraint = checkFile(file, timeConstraint);
			if (!resource_constraint)
				resource_constraint = checkFile(file, resourceConstraint);
			// LTL
		} else if (type.equals(UMLetDiagram.syssqc)) {
			syssqcDiagram = true;
			syssqcDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.syssqc);
			all_filenames.put(fileName, UMLetDiagram.syssqc);
			if (!time_constraint)
				time_constraint = checkFile(file, timeConstraint);
			if (!resource_constraint)
				resource_constraint = checkFile(file, resourceConstraint);
			// LTL
		} else if (type.equals(UMLetDiagram.uc)) {
			ucDiagram = true;
			ucDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.uc);
			all_filenames.put(fileName, UMLetDiagram.uc);
		} else if (type.equals(UMLetDiagram.dplmt)) {
			dplmtDiagram = true;
			dplmtDiagramPath.add(file);
			all_files.put(file, UMLetDiagram.dplmt);
			all_filenames.put(fileName, UMLetDiagram.dplmt);
		} else if (type.equals(UMLetDiagram.constraint)) {
			constraintFile = true;
			LTL_constraint = true;
			constraintFilePath.add(file);
			all_files.put(file, UMLetDiagram.constraint);
			all_filenames.put(fileName, UMLetDiagram.constraint);
		} else {
			throw new RuntimeException("Unknown Input UML Type!");
		}
	}

	/**
	 * 验证相关内容
	 * 
	 * @param num
	 * @return
	 */
	public static boolean verify(int n) {
		switch (n) {
		case 0:
			return getComp1();
		case 1:
			return getComp2();
		case 2:
			return getComp3();
		case 3:
			return getComp4();
		case 4:
			return getHier1();
		case 5:
			return getHier2();
		case 6:
			return getHier3();
		case 7:
			return getStrc1();
		case 8:
			return getStrc2(true);
		case 9:
			return getStrc3();
		case 10:
			return getFunc1();
		case 11:
			return getFunc2();
		case 12:
			return getFunc3();
		}
		return false;
	}

	/**
	 * 输入完整性1.架构必备组件图
	 * 
	 * @return
	 */
	private static boolean getComp1() {
		return cmpnDiagram;
	}

	/**
	 * 输入完整性2.4+1视图完备
	 * 
	 * @return
	 */
	private static boolean getComp2() {
		return (clsDiagram || sqcDiagram || syssqcDiagram) && actDiagram
				&& dplmtDiagram && (cmpnDiagram || pkgDiagram) && ucDiagram;
	}

	/**
	 * 输入完整性3.所有UMLet视图完备
	 * 
	 * @return
	 */
	private static boolean getComp3() {
		return clsDiagram && syssqcDiagram && sqcDiagram && actDiagram
				&& dplmtDiagram && cmpnDiagram && pkgDiagram && ucDiagram
				&& stateDiagram;
	}

	/**
	 * 输入完整性4.组件图中每个组件均有相应类图、系统顺序图中每个操作均有顺序图 未考虑多个组件图中组件重复的情况（以组件名称标识？）
	 * 未考虑系统顺序图中的返回操作是否不应统计
	 * 
	 * @return
	 */
	private static boolean getComp4() {
		int comps = 0;// number of components in all component diagrams
		if (cmpnDiagramPath.size() == 0)
			return false;
		if (syssqcDiagramPath == null)
			return false;
		for (String s : cmpnDiagramPath) {
			Diagram dg = new Diagram();
			dg.readXML(s);
			UMLERDiagram umlErDiagram = new UMLERDiagram(dg);
			comps += umlErDiagram.ue.size();
		}
		if (clsDiagramPath.size() < comps)
			return false;

		int sysOps = 0;

		for (String syssqc : syssqcDiagramPath) {
			Diagram dg = new Diagram();
			dg.readXML(syssqc);
			UMLERDiagram umlErDiagram = new UMLERDiagram(dg);
			int thisSys = umlErDiagram.ur.size();
			if (thisSys > sysOps)
				sysOps = thisSys;
		}
		if (sqcDiagramPath.size() < sysOps)
			return false;

		return true;
	}

	/**
	 * (组件图)层次性1.组件无直接循环依赖
	 * 
	 * @return
	 */
	private static boolean getHier1() {
		if (!cmpnDiagram)
			return false;
		for (String s : cmpnDiagramPath) {
			Diagram dg = new Diagram();
			dg.readXML(s);
			UMLERDiagram umlErDiagram = new UMLERDiagram(dg);
			UMLRelation[] urs = new UMLRelation[umlErDiagram.ur.size()];
			umlErDiagram.ur.toArray(urs);
			for (int i = 0; i < urs.length - 1; i++)
				for (int j = i + 1; j < urs.length; j++)
					if (urs[i].getStartEntityID() == urs[j].getEndEntityID())
						if (urs[j].getStartEntityID() == urs[i]
								.getEndEntityID())
							return false;
		}
		return true;
	}

	/**
	 * (组件图)层次性2.符合正交体系
	 * 
	 * @return
	 */
	private static boolean getHier2() {
		if (!cmpnDiagram)
			return false;

		return false;
	}

	/**
	 * (组件图)层次性3.单向依赖
	 * 
	 * @return
	 */
	private static boolean getHier3() {
		if (!cmpnDiagram)
			return false;
		for (String s : cmpnDiagramPath) {
			Diagram dg = new Diagram();
			dg.readXML(s);
			UMLERDiagram umlErDiagram = new UMLERDiagram(dg);
			ArrayList<Integer> compIDs = new ArrayList<Integer>();
			for (UMLEntity ue : umlErDiagram.ue)
				compIDs.add(ue.getId());
			boolean cyclic = false;
			L: for (int i = 0; i < compIDs.size(); i++) {// check if compIDs[i]
															// could reach
															// itself
				int currentID = compIDs.get(i);
				ArrayList<Integer> reachable = new ArrayList<Integer>();
				ArrayList<Integer> inc = new ArrayList<Integer>();
				inc.add(currentID);
				while (inc.size() > 0) {
					int x = inc.remove(0);
					reachable.add(x);
					for (UMLRelation ur : umlErDiagram.ur) {
						if (ur.getStartEntityID() == x) {
							int y = ur.getEndEntityID();
							if (y == currentID) {
								cyclic = true;
								break L;
							}
							if (y == -1)
								continue;
							if (reachable.contains(y))
								continue;
							if (inc.contains(y))
								continue;
							if (compIDs.indexOf(y) < compIDs.indexOf(currentID))
								continue;
							inc.add(y);
						}
					}
				}
			}
			if (cyclic)
				return false;
		}
		return true;
	}

	/**
	 * 结构性1.定义了组件接口
	 * 
	 * @return
	 */
	private static boolean getStrc1() {
		if (!cmpnDiagram)
			return false;
		for (String s : cmpnDiagramPath) {
			boolean r1 = checkFile(s,
					"<panel_attributes>lt=&lt;[v]-(()-[^]&gt;</panel_attributes>");
			boolean r2 = checkFile(s, "<panel_attributes>lt=&lt;[].&gt;()-",
					"&lt;&lt;delegate&gt;&gt;</panel_attributes>");
			boolean r3 = checkFile(s, "<panel_attributes>lt=&lt;[]&lt;.)-",
					"&lt;&lt;delegate&gt;&gt;</panel_attributes>");
			if (!(r1 | r2 | r3))
				return false;
		}
		return true;
	}

	/**
	 * 结构性2.组件图和系统顺序图中实体、关系说明不留空
	 * 
	 * @param deepcheck
	 *            彻底检查panel attributes
	 * @return
	 */
	private static boolean getStrc2(boolean deepcheck) {
		if (!cmpnDiagram)
			return false;
		String t1 = "<panel_attributes/>";
		String t2 = "<panel_attributes></panel_attributes>";
		String t3 = "<panel_attributes>\r\n</panel_attributes>";

		for (String file : all_files.keySet()) {
			if (checkFile(file, t1))
				return false;
			if (checkFile(file, t2))
				return false;
			if (checkFile(file, t3))
				return false;
		}

		if (deepcheck)
			for (String file : all_files.keySet()) {
				Diagram dg = new Diagram();
				dg.readXML(file);
				UMLERDiagram umlErDiagram = new UMLERDiagram(dg);
				for (UMLEntity u : umlErDiagram.ue) {
					String s = u.UMLEntityElement.getPanel_attributes();
					if (!nonEmpty(s))
						return false;
				}
				for (UMLRelation u : umlErDiagram.ur) {
					String s = u.UMLRelationElement.getPanel_attributes();
					if (!nonEmpty(s))
						return false;
				}
			}

		return true;
	}

	/**
	 * 检查panel attributes是否包含有意义的字符
	 * 
	 * @param s
	 * @return
	 */
	private static boolean nonEmpty(String s) {
		if (s == null)
			return false;
		String test = s.replaceAll("lt", "");
		char[] ct = test.toCharArray();
		for (char c : ct) {
			if ((c >= 'a') && (c <= 'z'))
				return true;
			if ((c >= 'A') && (c <= 'Z'))
				return true;
			if ((c >= '0') && (c <= '9'))
				return true;
		}
		return false;
	}

	/**
	 * 结构性3.使用Notation标注
	 * 
	 * @return
	 */
	private static boolean getStrc3() {
		if (!cmpnDiagram)
			return false;
		String t1 = "com.umlet.element.Note";

		for (String file : all_files.keySet())
			if (checkFile(file, t1))
				return true;

		return false;
	}

	/**
	 * 功能性1.时间约束time=|time =
	 * 
	 * @return
	 */
	private static boolean getFunc1() {
		return time_constraint;
	}

	/**
	 * 功能性2.资源约束resource=|resource =
	 * 
	 * @return
	 */
	private static boolean getFunc2() {
		return resource_constraint;
	}

	/**
	 * 功能性3.LTL约束
	 * 
	 * @return
	 */
	private static boolean getFunc3() {
		return LTL_constraint;
	}

	/**
	 * check if file has the string "check"
	 * 
	 * @param filepath
	 * @param check
	 * @return
	 */
	private static boolean checkFile(String filepath, String check) {
		File f = new File(filepath);
		if (!f.exists()) {
			System.err.println("No input File");
			return false;
		}
		try {
			BufferedReader dr = new BufferedReader(new InputStreamReader(
					new FileInputStream(f)));
			String line = new String();
			while ((line = dr.readLine()) != null) {
				if (line.indexOf(check) > 0) {
					dr.close();
					return true;
				}
			}
			dr.close();
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * check if file continuously has the strings "checks"
	 * 
	 * @param filepath
	 * @param check1
	 * @param check2
	 * @return
	 */
	private static boolean checkFile(String filepath, String check1,
			String check2) {
		File f = new File(filepath);
		if (!f.exists()) {
			System.err.println("No input File");
			return false;
		}
		try {
			BufferedReader dr = new BufferedReader(new InputStreamReader(
					new FileInputStream(f)));
			boolean b1 = false;
			String line = new String();
			while ((line = dr.readLine()) != null) {
				if (b1) {
					if (line.indexOf(check2) >= 0) {
						dr.close();
						return true;
					}
				} else {
					if (line.indexOf(check1) >= 0) {
						b1 = true;
					} else
						b1 = false;
				}
			}
			dr.close();
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
}
